package play;

import lombok.NoArgsConstructor;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.tree.ParseTree;

import java.util.*;

/**
 * 注释树。
 * 语义分析的结果都放这里。跟 AST 的节点建立关联。包括
 * 1。类型信息，包括基本类型和用户自定义类型。
 * 2。变量和函数调用的消解。
 * 3。作用域 scope。在 scope 中包含了该作用域的所有符号。variable，function，class 等都是符号
 */

@NoArgsConstructor
public class AnnotatedTree {
    // AST
    protected ParseTree ast = null;

    // 解析出来的所有类型，包括类和函数，以后还可以包括数组、枚举。类的方法也作为单独的要素放进去。
    protected List<Type> types = new LinkedList<>();

    // AST 节点对应的 Symbol
    protected Map<ParserRuleContext, Symbol> symbolOfNode = new HashMap<>();

    // AST 节点对应的 Scope，如 for、函数调会启动新的 Scope
    protected Map<ParserRuleContext, Scope> node2Scope = new HashMap<>();

    // 用于做类型推断，每个节点推断出来的类型
    protected Map<ParserRuleContext, Type> typeOfNode = new HashMap<>();

    // 全局命名空间
    NameSpace nameSpace = null;

    // 语义分析过程中生成的信息，包括普通信息、警告、错误
    protected List<CompilationLog> logs = new LinkedList<>();

    // 在构造函数里，引用的 this().第二个函数是被调用的构造函数
    protected Map<Function, Function> thisConstructorRef = new HashMap<>();

    // 在构造函数里，引用的 super().第二个函数是被调用的构造函数
    protected Map<Function, Function> superConstructorRef = new HashMap<>();

    /**
     * 记录编译错误和警告
     * @param message 消息
     * @param type 消息类型， CompilationLog 中的 info，warning，error
     * @param ctx 语义上下文
     */
    protected void log(String message, int type, ParserRuleContext ctx) {
        CompilationLog log = new CompilationLog();
        log.ctx = ctx;
        log.message = message;
        log.line = ctx.getStart().getLine();
        log.positionInLine = ctx.getStart().getStartIndex();
        log.type = type;

        logs.add(log);
        System.out.println(log);
    }

    public void log(String message, ParserRuleContext ctx) {
        this.log(message, CompilationLog.ERROR, ctx);
    }

    /**
     * 是否有编译错误
     * @return bool
     */
    protected boolean hasCompilationError() {
        for (CompilationLog log : logs) {
            if (log.type == CompilationLog.ERROR) {
                return true;
            }
        }
        return false;
    }

    /**
     * 通过名称查找 Variable。逐级 Scope 查找。
     * @param scope 起始 scope。
     * @param idName 需要查找的 idName
     * @return 变量
     */
    protected Variable lookupVariable(Scope scope, String idName) {
        Variable rtn = scope.getVariable(idName);
        if (rtn == null && scope.enclosingScope != null) {
            rtn = lookupVariable(scope.enclosingScope, idName);
        }
        return rtn;
    }

    /**
     * 通过名称查找 Class。逐级 Scope 查找。
     * @param scope 起始 scope
     * @param idName 查找的 idName
     * @return Class
     */
    protected Class lookupClass(Scope scope, String idName) {
        Class rtn = scope.getClass(idName);
        if (rtn == null && scope.enclosingScope != null) {
            rtn = lookupClass(scope.enclosingScope, idName);
        }
        return rtn;
    }

    // FIXME 单纯根据名称并不严密

    /**
     * 通过名称查找Type
     * @param idName 查找的名称
     * @return Type
     */
    protected Type lookupType(String idName) {
        Type rtn = null;
        for (Type type : types) {
            if (type.getName().equals(idName)) {
                rtn = type;
                break;
            }
        }
        return rtn;
    }

    /**
     * 通过方法的名称和方法签名查找 Function，逐级 Scope 查找
     * @param scope 起始 scope
     * @param idName 查找名
     * @param paramTypes 参数
     * @return Function or null
     */
    protected Function lookupFunction(Scope scope, String idName, List<Type> paramTypes) {
        Function rtn = scope.getFunction(idName, paramTypes);
        if (rtn == null && scope.enclosingScope != null){
            rtn = lookupFunction(scope.enclosingScope, idName, paramTypes);
        }
        return rtn;
    }

    /**
     * 查找函数型变量，逐级查找。
     * @param scope 起始作用域
     * @param idName 名称
     * @param paramTypes 参数
     * @return Variable or null if does not find it.
     */
    protected Variable lookupFunctionVariable(Scope scope, String idName, List<Type> paramTypes) {
        Variable rtn = scope.getFunctionVariable(idName, paramTypes);
        if (rtn == null && scope.enclosingScope != null) {
            rtn = lookupFunctionVariable(scope.enclosingScope, idName, paramTypes);
        }
        return rtn;
    }

    /**
     * 逐级查找函数(或方法)。仅通过名字查找，如果有重名的，返回第一个。。。
     * FIXME 解决重复问题
     * @param scope 起始作用域
     * @param name 名称
     * @return Function or null;
     */
    protected Function lookupFunction(Scope scope, String name) {
        Function rtn;
        if (scope instanceof Class) {
            rtn = getMethodOnlyByName((Class)scope, name);
        } else {
            rtn = getFunctionOnlyByName(scope, name);
        }
        if (rtn == null && scope.enclosingScope != null) {
            rtn = lookupFunction(scope.enclosingScope, name);
        }
        return rtn;
    }

    /**
     * 查找某节点所在的 Scope。
     * 算法：逐级查找父节点，找到一个对应着 Scope的上级节点
     * @param node 节点
     * @return scope
     */
    public Scope enclosingScopeOfNode(ParserRuleContext node) {
        Scope rtn = null;
        ParserRuleContext parent = node.getParent();
        if (parent != null) {
            rtn = node2Scope.get(parent);
            if (rtn == null) {
                rtn = enclosingScopeOfNode(parent);
            }
        }
        return rtn;
    }

    /**
     * 包含某节点的函数
     * @param ctx 节点
     * @return 函数
     */
    public Function enclosingFunctionOfNode(RuleContext ctx) {
        if (ctx.parent instanceof PlayScriptParser.FunctionDeclarationContext) {
            return (Function) node2Scope.get(ctx.parent);
        } else if (ctx.parent == null) {
            return null;
        } else {
            return enclosingFunctionOfNode(ctx.parent);
        }
    }

    /**
     * 包含某节点的类
     * @param ctx 节点
     * @return 节点
     */
    public Class enclosingClassOfNode(RuleContext ctx) {
        if (ctx.parent instanceof PlayScriptParser.ClassDeclarationContext) {
            return (Class) node2Scope.get(ctx.parent);
        } else if (ctx.parent == null) {
            return null;
        } else {
            return enclosingClassOfNode(ctx.parent);
        }
    }

    /**
     * 输出本Scope中的内容，包括每个变量的名称、类型。
     * @return 树状显示的字符串
     */
    public String getScopeTreeString() {
        StringBuilder sb = new StringBuilder();
        scopeToString(sb, nameSpace, "");
        return sb.toString();
    }

    private void scopeToString(StringBuilder sb, Scope scope, String indent) {
        sb.append(indent).append(scope).append('\n');
        for (Symbol symbol : scope.symbols) {
            if (symbol instanceof Scope) {
                scopeToString(sb, (Scope) symbol, indent+'\t');
            } else {
                sb.append(indent).append('\t').append(symbol).append('\n');
            }
        }
    }

    // 类中的函数, 需要同时查找父类
    private Function getMethodOnlyByName(Class theClass, String name) {
        Function rtn = getFunctionOnlyByName(theClass, name);
        if (rtn == null && theClass.getParentClass() != null) {
            rtn = getMethodOnlyByName(theClass.getParentClass(), name);
        }
        return rtn;
    }

    private Function getFunctionOnlyByName(Scope scope, String name) {
        for (Symbol s : scope.symbols) {
            if (s instanceof Function && s.name.equals(name)) {
                return (Function) s;
            }
        }
        return null;
    }
}
